package cn.icanci.loopstack.bic.biz.service.impl;

import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

import javax.annotation.Resource;

import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;

import com.google.common.collect.Lists;
import com.google.common.collect.Sets;

import cn.icanci.loopstack.bic.biz.cache.GraphRepository;
import cn.icanci.loopstack.bic.biz.mapper.GraphBuryingMapper;
import cn.icanci.loopstack.bic.biz.mapper.GraphMapper;
import cn.icanci.loopstack.bic.biz.service.BuryingService;
import cn.icanci.loopstack.bic.biz.service.GraphService;
import cn.icanci.loopstack.bic.biz.utils.TemplateFileUtils;
import cn.icanci.loopstack.bic.common.enums.NodeDataModeEnum;
import cn.icanci.loopstack.bic.graph.Graph;
import cn.icanci.loopstack.bic.graph.GraphBurying;
import cn.icanci.loopstack.bic.graph.UnitViewGraph;
import cn.icanci.loopstack.bic.graph.edge.Edge;
import cn.icanci.loopstack.bic.graph.node.NodeAttrsLabel;
import cn.icanci.loopstack.bic.graph.node.UnitViewNode;
import lombok.Data;

/**
 * @author icanci
 * @since 1.0 Created in 2023/09/23 23:00
 */
@Service
public class GraphServiceImpl implements GraphService {
    @Resource
    private GraphRepository    graphRepository;
    @Resource
    private BuryingService     buryingService;
    @Resource
    private GraphMapper        graphMapper;
    @Resource
    private GraphBuryingMapper graphBuryingMapper;

    @Override
    public UnitViewGraph buildOneGraph(String systemUuid, String useCaseUuid, String traceId, String orderSerialNo) {
        Graph graph = graphRepository.get(systemUuid, useCaseUuid);
        UnitViewGraph unitViewGraph = graphMapper.do2vo(graph);
        if (unitViewGraph == null) {
            return null;
        }

        List<GraphBurying> graphBuryingList = graphBuryingMapper.vos2dos(buryingService.queryBySystemAndUseCase(systemUuid, useCaseUuid, traceId, orderSerialNo));
        // 数据集合
        Map<String, List<GraphBurying>> uniqueKeyMap = graphBuryingList.stream().collect(Collectors.groupingBy(GraphBurying::getUniqueKey));
        Set<String> nodeDataKeySet = Sets.newHashSet();
        // 根据前缀匹配和全部匹配进行值匹配
        for (UnitViewNode node : unitViewGraph.getNodes()) {
            doClearNode(node);
            NodeDataModeEnum nodeDataMode = NodeDataModeEnum.getEnumByCode(node.getNodeDataMode());
            String nodeDataKey = node.getNodeDataKey();
            node.setBuryings(buildBuryings(nodeDataMode, nodeDataKey, uniqueKeyMap));
            nodeDataKeySet.add(nodeDataKey);
        }
        // 图渲染，构建额外参数
        unitViewGraphRender(unitViewGraph);
        // 尝试新增游离节点
        tryAddFreeNode(unitViewGraph, nodeDataKeySet, uniqueKeyMap);
        return unitViewGraph;
    }

    private void doClearNode(UnitViewNode node) {
        node.setBuryings(null);
        node.setPathPass(false);
        NodeAttrsLabel label = node.getAttrs().getLabel();
        String resetName = label.getText().replace("(无数据)", StringUtils.EMPTY);
        label.setText(resetName);
    }

    private void tryAddFreeNode(UnitViewGraph unitViewGraph, Set<String> nodeDataKeySet, Map<String, List<GraphBurying>> uniqueKeyMap) {
        for (String nodeDataKey : nodeDataKeySet) {
            uniqueKeyMap.remove(nodeDataKey);
        }
        if (uniqueKeyMap.isEmpty()) {
            return;
        }
        // 构建游离节点
        for (Map.Entry<String, List<GraphBurying>> entry : uniqueKeyMap.entrySet()) {
            String key = entry.getKey();
            UnitViewNode node = TemplateFileUtils.buildDefaultNode(key);
            node.setBuryings(entry.getValue());
            node.setPathPass(true);
            unitViewGraph.getNodes().add(node);
        }
    }

    private void unitViewGraphRender(UnitViewGraph unitViewGraph) {
        Map<String, UnitViewNode> nodeMap = unitViewGraph.getNodes().stream().collect(Collectors.toMap(UnitViewNode::getId, node -> node));
        // 组装边节点的临时变量
        List<EdgeRelation> edgeRelations = Lists.newArrayList();
        for (Edge edge : unitViewGraph.getEdges()) {
            UnitViewNode source = nodeMap.get(edge.getSource().getCell());
            UnitViewNode target = nodeMap.get(edge.getTarget().getCell());
            EdgeRelation edgeRelation = new EdgeRelation();
            edgeRelation.setSource(source);
            edgeRelation.setTarget(target);
            edgeRelation.setEdge(edge);
            edgeRelations.add(edgeRelation);
        }
        // 对EdgeRelation进行排序，排序从大到小，会有个深度的计算
        edgeRelations = edgeRelations.stream().sorted((o1, o2) -> o2.getEdge().getDeep() - o1.getEdge().getDeep()).collect(Collectors.toList());
        // 组装边节点的临时变量
        for (EdgeRelation edgeRelation : edgeRelations) {
            UnitViewNode target = edgeRelation.getTarget();
            if (target.hasPathPass()) {
                edgeRelation.getSource().setPathPass(true);
            }
        }

        for (UnitViewNode node : unitViewGraph.getNodes()) {
            if (node.hasPathPass()) {
                continue;
            }
            String text = node.getAttrs().getLabel().getText();
            String noData = "(无数据)";
            text = text.replace(noData, StringUtils.EMPTY);
            node.getAttrs().getLabel().setText(text + noData);
        }
    }

    private List<GraphBurying> buildBuryings(NodeDataModeEnum nodeDataMode, String nodeDataKey, Map<String, List<GraphBurying>> uniqueKeyMap) {
        // 构建当前匹配key的数据值
        if (nodeDataMode == NodeDataModeEnum.PREFIX) {
            return uniqueKeyMap.entrySet().stream().filter(entry -> entry.getKey().startsWith(nodeDataKey)).map(Map.Entry::getValue).flatMap(List::stream)
                .collect(Collectors.toList());
        } else if (nodeDataMode == NodeDataModeEnum.ALL) {
            return uniqueKeyMap.entrySet().stream().filter(entry -> entry.getKey().equals(nodeDataKey)).map(Map.Entry::getValue).flatMap(List::stream).collect(Collectors.toList());
        }
        return Lists.newArrayList();
    }

    @Data
    private static class EdgeRelation {
        private UnitViewNode source;
        private UnitViewNode target;
        private Edge         edge;
    }
}
